#pragma once

#include <limits>

#include "ast.h"
#include "pass.h"
#include "token.h"

class Parser : public Pass {
    using Tag = Token::Tag;

    std::list<Token::BaseToken*> tokens;
    std::list<Token::BaseToken*>::iterator it;

    Location Loc() const;

    bool Empty() const { return it == tokens.end(); }
    Token::BaseToken* Peek() const { return Empty() ? nullptr : *it; }
    void Prev() { it == tokens.begin() ? it : --it; }
    Token::BaseToken* Next() { return Empty() ? nullptr : *it++; }

    bool Check(Tag tag) { return Peek()->tag == tag; }
    bool Try(Tag tag) { return Check(tag) && (Next(), true); }
    Token::BaseToken* Expect(Tag tag);

    template <class T>
    T* Check() {
        return Peek()->As<T>();
    }

    template <class T, class... Args>
    T* NewNode(Args&&... args) {
        auto* ret = new T(args...);
        ret->loc = Loc();
        return ret;
    }

  public:
    AST::Block* ParseBlock();

    AST::ASTNode* ParseStmt();

    AST::IfStmt* ParseIfStmt();

    AST::WhileStmt* ParseWhileStmt();

    std::vector<AST::ASTNode*> ParseDecl();

    std::vector<AST::ASTNode*> ParseVarDecl(bool isConst, Tag baseType);

    AST::InitializerExpr* ParseInitializer();

    AST::FuncDecl* ParseFuncDecl(Tag baseType);

    AST::ASTNode* ParseExpr(Tag exitTag = Tag::End,
                            int exitPri = std::numeric_limits<int>().max());

  public:
    AST::Block* Parse();

    void* Run(void* input) override;
};
